#define _GNU_SOURCE 1
#include <sched.h> //for binding process to processor

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>




#define WORKER_QUEUE 100
#define BUFFER_SIZE 512


char workers_dir[108];

int start_worker(int index)
{
	int local_socket;
	if ((local_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		exit(1);
	}
	char worker_path[208];
	bzero(worker_path, 208);
	snprintf(worker_path, 208, "%s/w%d", workers_dir, index);
	fprintf(stderr, "worker path : %s , worker pid: %d \n", worker_path, getpid());
	struct sockaddr_un local;
	local.sun_family = AF_UNIX;
	strcpy(local.sun_path, worker_path);
	unlink(local.sun_path);
	int len = sizeof(local);
	if (bind(local_socket, (struct sockaddr *)&local, len) == -1) {
		perror("bind");
		exit(1);
	}

	if (listen(local_socket, WORKER_QUEUE) == -1) {
		perror("listen");
		exit(1);
	}

	//echo server
	for(;;) {
		struct sockaddr_un remote;
		int r_len = sizeof(remote);
		int remote_socket;
		if ((remote_socket = accept(local_socket, (struct sockaddr *)&remote, &r_len)) == -1) {
			perror("accept");
			exit(1);
		}

		int done = 0;
		do {
			char buffer[BUFFER_SIZE];
			bzero(buffer, BUFFER_SIZE);
			int n = recv(remote_socket, buffer, BUFFER_SIZE, 0);
			if (n <= 0) {
				if (n < 0) perror("recv");
				done = 1;
				break;
			}else{
				if (send(remote_socket, buffer, n, 0) < 0) {
					perror("send");
					done = 1;
					break;
				}
			}
		} while (!done);

		close(remote_socket);
	}
	close(local_socket);

	return 0;
}
int main(int argc, char *argv[]){
	int pid = getpid();
	if(argc < 3){
		printf("usage: echo-server <number of workers to start> <path to workers socket directory> [numberOfCPUS:coma-separated processors list]");
		printf("argc: %d", argc);
		exit(-1);
	}

	/*bind to specific processor*/
	if(argv[3] != NULL){
		cpu_set_t* cpu_set;
		char* p = NULL;
		int num_of_cpus = 0;
		if((p = strtok (argv[3],":")) == NULL){
			perror("split error");
			return;
		}else{
			num_of_cpus = atoi(p);
			cpu_set = CPU_ALLOC(num_of_cpus);
		}
		size_t size = CPU_ALLOC_SIZE(num_of_cpus);

		CPU_ZERO_S(size, cpu_set);
		while((p = strtok (NULL, ",")) != NULL){
			CPU_SET_S(atoi(p), size, cpu_set);
		}

		if (sched_setaffinity(pid, size, cpu_set) < 0) {
			perror("sched_setaffinity");
		}

		if (sched_getaffinity(pid, size, cpu_set) < 0) {
			perror("sched_getaffinity");
			return -1;
		}
		printf("pid %d's affinity mask: %d\n",pid, *cpu_set);
		CPU_FREE(cpu_set);
	}

	printf("workers manager id: %d\n", pid);
	fflush(stdout);

	int number_of_wokers = atoi(argv[1]);
	strcpy(workers_dir, argv[2]);
	int worker_index = 0;
	for(worker_index = 0; worker_index < number_of_wokers; worker_index++){
		if (fork() == 0) {//child
			start_worker(worker_index);
			return 0;
		} else {//parent
			continue;
		}
	}
	//wait to all children to
	for(worker_index = 0; worker_index < number_of_wokers; worker_index++){
		wait(NULL);
	}
	return 0;
}
